/***
 * Copyright (c) 2021-2031 murenchao
 * fig is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *       http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */
package cool.taomu.software.fig.crypto;

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.UUID;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The Class DES3.
 */
public class Des3 implements ICrypto {
	
	final Logger LOG = LoggerFactory.getLogger(Des3.class);

    /** The default iv. */
    private byte[] defaultIv =
        {0x32, (byte)0x46, (byte)0xfe, (byte)0xab, (byte)0xfc, 0x2d, (byte)0x15, (byte)0xfa};

    /** The Constant ENCODING. */
    private static final String ENCODING = "UTF-8";

    /** The Constant key. */
    private String key = "70rEwsfQZBUW+LB2l16RfMgcARMHqDsV";

    private ICrypto c;
    
    private byte[] src;

    public Des3(byte[] src) {
    	this.src = src;
    }

    public Des3(String key, ICrypto c) {
        this.key = key;
        this.c = c;
    }
    
    public Des3(String key,byte[] iv,ICrypto c) {
        this.key = key;
        this.c = c;
        this.defaultIv = iv;
    }

    public Des3(ICrypto c) {
        this.c = c;
    }

    /**
     * des加密.
     */
    private byte[] encryptBytes(byte[] src) throws Exception {
        DESedeKeySpec desks = new DESedeKeySpec(this.key.getBytes("UTF-8"));
        SecretKeyFactory desEde = SecretKeyFactory.getInstance("DESede");
        SecretKey securekey = desEde.generateSecret(desks);
        Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
        IvParameterSpec ivSpec = new IvParameterSpec(defaultIv);
        cipher.init(Cipher.ENCRYPT_MODE, securekey, ivSpec);
        return cipher.doFinal(src);
    }

    /**
     * des解密.
     *
     */
    private byte[] decryptBytes(byte[] srcBytes) throws Exception {
        DESedeKeySpec desks = new DESedeKeySpec(this.key.getBytes(ENCODING));
        SecretKeyFactory desEde = SecretKeyFactory.getInstance("DESede");
        SecretKey securekey = desEde.generateSecret(desks);
        IvParameterSpec ivSpec = new IvParameterSpec(defaultIv);
        Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, securekey, ivSpec);
        return cipher.doFinal(srcBytes);
    }

    /**
     * Gen key.

     */
    public static String createDes3Key() throws NoSuchAlgorithmException {
        byte[] keyBytes = UUID.randomUUID().toString().getBytes();
        KeyGenerator desEde = KeyGenerator.getInstance("DESede");
        desEde.init(new SecureRandom(keyBytes));
        SecretKey key = desEde.generateKey();
        ICrypto base64 = new Base64(key.getEncoded());
        return new String(base64.encode());
    }

    @Override
    public byte[] encode() {
        if (c != null) {
            try {
                return this.encryptBytes(c.encode());
            } catch (Exception e) {
            	LOG.info("encode : ",e);
            }
        }
        try {
            return this.encryptBytes(src);
        } catch (Exception e) {
        	LOG.info("encode : ",e);
        }
        return null;
    }

    @Override
    public byte[] decode() {
        try {
            if (c != null) {
            	byte[] des3Src = this.decryptBytes(c.getData());
            	c.setData(des3Src);
                return c.decode();
            }else {
            	return this.decryptBytes(this.src);
            }
        } catch (Exception e) {
        	LOG.info("decode : ",e);
        }
        return null;
    }

	@Override
	public byte[] getData() {
		return this.src;
	}

	@Override
	public ICrypto setData(byte[] src) {
		this.src = src;
		return this;
	}
}
